#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Datetime: 2020/12/24 19:41
# @Author  : CHEN Wang
# @Site    : 
# @File    : campisi_attribution.py
# @Software: PyCharm 

"""
脚本说明: 基金campisi归因模块
"""

import numpy as np
import pandas as pd
from quant_researcher.quant.datasource_fetch.fund_api.fund_holding_related import get_holding_report_info
from quant_researcher.quant.performance_attribution.core_functions.attribution_analysis.campisi.portfolio_holding_campisi_attribution import get_campisi_attribution
from quant_researcher.quant.datasource_fetch.index_api.index_info import get_sw_industry_name
from quant_researcher.quant.performance_attribution.core_functions.attribution_analysis import multi_period_attribution
from quant_researcher.quant.performance_attribution.fund_related.ret_decomposition.asset_ret_decomposition import fund_asset_decomposition


def fund_campisi_attribution(fund_code='070002', start_date='2019-12-31', end_date='2020-06-30', **kwargs):
    conn_base = kwargs.pop('conn_base', None)
    conn_ty = kwargs.pop('conn_ty', None)
    conn_stock = kwargs.pop('conn_stock', None)
    # 获取基金期初期末债券持仓信息，注意哪怕是半年报年报，债券持仓信息也只有部分
    bond_p_holding_begin = get_holding_report_info(report='bond', fund_code=[fund_code], report_type=[5, 6],
                                                   end_date=start_date,
                                                   select=['bond_code as security_code', 'bond_sname','pptinnv/100 as security_weight'],
                                                   conn=conn_base)
    bond_p_holding_end = get_holding_report_info(report='bond', fund_code=[fund_code], report_type=[5, 6],
                                                 end_date=end_date,
                                                 select=['bond_code as security_code', 'bond_sname', 'pptinnv/100 as security_weight'],
                                                 conn=conn_base)
    # 根据债券持仓进行campisi业绩归因
    bond_attribution_df, total_attribution = get_campisi_attribution(fund_code=fund_code,
                                                                     start_date=start_date,
                                                                     end_date=end_date,
                                                                     holding_begin=bond_p_holding_begin,
                                                                     holding_end=bond_p_holding_end,
                                                                     conn_base=conn_base,
                                                                     conn_ty=conn_ty,
                                                                     conn_stock=conn_stock)

    return bond_attribution_df, total_attribution


def fund_campisi_attribution_multi_period(fund_code='002354', start_date='2019-06-30', end_date='2020-06-30', **kwargs):
    conn_base = kwargs.pop('conn_base', None)
    conn_ty = kwargs.pop('conn_ty', None)
    conn_stock = kwargs.pop('conn_stock', None)

    date_range = pd.date_range(start_date, end_date, freq='Q')
    date_to_calc = date_range[date_range.month.isin([6, 12])].strftime('%Y-%m-%d').tolist()
    attribution_list = []
    bond_attribution_list = []
    for start, end in zip(date_to_calc[:-1], date_to_calc[1:]):
        bond_attribution_df, total_attribution = fund_campisi_attribution(
            fund_code=fund_code, start_date=start, end_date=end,
            conn_base=conn_base, conn_ty=conn_ty, conn_stock=conn_stock)
        total_attribution = total_attribution.rename(end)
        attribution_list.append(total_attribution)
        bond_attribution_list.append(bond_attribution_df)
    attribution_df = pd.concat(attribution_list, axis=1).T
    bond_attribution_df = pd.concat(bond_attribution_list)

    period_attribution_list = []
    period_bond_attribution_list = []
    for date in attribution_df.index:
        # 进行进行多期Campisi收益统计
        attribution_df_temp = attribution_df.loc[:date, ['all_income_effect', 'all_treasurybond_effect', 'all_credit_effect', 'all_other_effect', 'all_bond_ret']]
        fund_ret_series = attribution_df.loc[:date, 'fund_total_return']
        multi_period_coefficient = multi_period_attribution.absolute_multi_period(fund_ret_series)  # 获取多期归因系数

        weighted_ret = attribution_df_temp.mul(multi_period_coefficient, axis=0)
        cummulated_ret = weighted_ret.sum(axis=0)
        cummulated_ret['end_date'] = date
        period_attribution_list.append(cummulated_ret)

        # 分行业进行多期Brinson收益统计
        bond_attribution_df_temp = bond_attribution_df[bond_attribution_df['end_date'] <= date]
        bond_attribution_df_temp = bond_attribution_df_temp.reset_index().set_index(['end_date'])
        bond_attribution_df_temp = bond_attribution_df_temp.merge(pd.DataFrame(multi_period_coefficient), right_index=True, left_index=True, how='left')
        bond_attribution_df_temp.iloc[:, 1:-2] = bond_attribution_df_temp.iloc[:, 1:-2].mul(bond_attribution_df_temp.iloc[:, -1], axis=0)
        cummulate_bond_ret = bond_attribution_df_temp.groupby('index').sum()
        cummulate_bond_ret.drop(columns=['multi_period_coff'], inplace=True)
        cummulate_bond_ret['end_date'] = date
        period_bond_attribution_list.append(cummulate_bond_ret)

    period_attribution = pd.DataFrame(period_attribution_list).set_index(['end_date'])
    period_attribution.loc[start_date, :] = 0  # 把初始收益率0加进去
    period_attribution.sort_index(inplace=True)

    period_bond_attribution = pd.concat(period_bond_attribution_list, axis=0)
    bond_sname = bond_attribution_df.loc[:, ['bond_sname']].drop_duplicates()
    period_bond_attribution = period_bond_attribution.merge(bond_sname, right_index=True, left_index=True, how='left')

    # 获取从起始日至今的累计收益率归因
    latest_attribution = period_attribution.iloc[-1]

    # 获取从起始日至今的各行业累计收益率归因
    latest_bond_attribution = period_bond_attribution[
        period_bond_attribution['end_date'] == end_date]

    return latest_bond_attribution, latest_attribution, period_attribution


if __name__ == '__main__':
    fund_code = '002354'

    # 测试单期campisi
    start_date = '2019-12-31'
    end_date = '2020-06-30'
    test = fund_campisi_attribution(fund_code=fund_code, start_date=start_date, end_date=end_date)

    # 测试多期campisi
    start_date = '2019-06-30'
    end_date = '2020-06-30'
    test = fund_campisi_attribution_multi_period(fund_code=fund_code, start_date=start_date, end_date=end_date)